/*
 * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */
package java.lang;

import java.util.*;

/*
 * Class to track and run user level shutdown hooks registered through
 * <tt>{@link Runtime#addShutdownHook Runtime.addShutdownHook}</tt>.
 *
 * @see java.lang.Runtime#addShutdownHook
 * @see java.lang.Runtime#removeShutdownHook
 */

class ApplicationShutdownHooks {

  /* The set of registered hooks */
  private static IdentityHashMap<Thread, Thread> hooks;

  static {
    try {
      Shutdown.add(1 /* shutdown hook invocation order */,
          false /* not registered if shutdown in progress */,
          new Runnable() {
            public void run() {
              runHooks();
            }
          }
      );
      hooks = new IdentityHashMap<>();
    } catch (IllegalStateException e) {
      // application shutdown hooks cannot be added if
      // shutdown is in progress.
      hooks = null;
    }
  }


  private ApplicationShutdownHooks() {
  }

  /* Add a new shutdown hook.  Checks the shutdown state and the hook itself,
   * but does not do any security checks.
   */
  static synchronized void add(Thread hook) {
    if (hooks == null) {
      throw new IllegalStateException("Shutdown in progress");
    }

    if (hook.isAlive()) {
      throw new IllegalArgumentException("Hook already running");
    }

    if (hooks.containsKey(hook)) {
      throw new IllegalArgumentException("Hook previously registered");
    }

    hooks.put(hook, hook);
  }

  /* Remove a previously-registered hook.  Like the add method, this method
   * does not do any security checks.
   */
  static synchronized boolean remove(Thread hook) {
    if (hooks == null) {
      throw new IllegalStateException("Shutdown in progress");
    }

    if (hook == null) {
      throw new NullPointerException();
    }

    return hooks.remove(hook) != null;
  }

  /* Iterates over all application hooks creating a new thread for each
   * to run in. Hooks are run concurrently and this method waits for
   * them to finish.
   */
  static void runHooks() {
    Collection<Thread> threads;
    synchronized (ApplicationShutdownHooks.class) {
      threads = hooks.keySet();
      hooks = null;
    }

    for (Thread hook : threads) {
      hook.start();
    }
    for (Thread hook : threads) {
      try {
        hook.join();
      } catch (InterruptedException x) {
      }
    }
  }
}
